package xfix;

import xfix.fitness.xbi.XbiFitnessFunction;
import xfix.input.xbi.XpertXbi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class EvaluateRootCause
{
	private RootCauseList chromosome;
	private RootCauseList initialChromosome;

	private String optimalValue;
	//对于一个XBI的所有rootCause，fitnessScore都是计算的XBI中的两个控件
	private Set<String> optimalPartXbi;
	private double[] optimalFitnessScore;//local

	private Map<String, List<OptimalRootCause>> optimalMap;	// <xpath, <prop, val, fitnessScore>>

	private boolean isSiblingAdjustment;
	private RootCauseList initialChromosomeWithoutSiblingAdjustment;
	private Map<String,Set<String>> historyProps;
	public EvaluateRootCause(RootCauseList initialChromosome, RootCauseList chromosome, XpertXbi xpertXbi,Map<String,Set<String>> historyProps)
	{
		this.chromosome = chromosome.copy();
		this.initialChromosome = chromosome.copy();
		this.optimalMap = new HashMap<>();
		this.initialChromosomeWithoutSiblingAdjustment = initialChromosome;
		this.historyProps = historyProps;
	}


	public double[] getOptimalFitnessScore() {
		return optimalFitnessScore;
	}

	public void setOptimalFitnessScore(double[] optimalFitnessScore) {
		this.optimalFitnessScore = optimalFitnessScore;
	}

	public RootCauseList getChromosome()
	{
		return chromosome;
	}

	public void setChromosome(RootCauseList chromosome)
	{
		this.chromosome = chromosome;
	}

	public RootCauseList getInitialChromosome()
	{
		return initialChromosome;
	}

	public void setInitialChromosome(RootCauseList initialChromosome)
	{
		this.initialChromosome = initialChromosome;
	}

	public String getOptimalValue()
	{
		return optimalValue;
	}

	public void setOptimalValue(String optimalValue)
	{
		this.optimalValue = optimalValue;
	}

	public Set<String> getOptimalPartXbi() {
		return optimalPartXbi;
	}

	public void setOptimalPartXbi(Set<String> optimalPartXbi) {
		this.optimalPartXbi = optimalPartXbi;
	}

	public Map<String, List<OptimalRootCause>> getOptimalMap()
	{
		return optimalMap;
	}

	public void setOptimalMap(Map<String, List<OptimalRootCause>> optimalMap)
	{
		this.optimalMap = optimalMap;
	}

	public void runGeneSearch(RootCause gene, int geneCount)
	{
		if(!gene.isProcess())
		{
			System.out.println("no process: " + gene.getXpath() + " -> " + gene.isProcess());
			return;
		}

		//optimalFitnessScore = initialChromosome.getFitnessScore();
		XbiFitnessFunction fitnessFunction = new XbiFitnessFunction();
		Set<String> initialPartXbi = fitnessFunction.getPartXbi(initialChromosome,gene);

		double[] initialFitnessScore = fitnessFunction.getFitnessScoreLocal(initialChromosome, gene);

		//XBI有些误报
//		if(Util.doubleArrayTooSmall(initialFitnessScore)){
//			return;
//		}

		System.out.println("Evaluate gene " + gene.getXpath() +" initial fitness score = " + initialFitnessScore);

		//历史纪录，不重复调整
		int rootCauseCount = 1;
		for(String prop : gene.getPropValueMap().keySet())
		{
			if(historyProps.containsKey(gene.getXpath())){
				Set<String> props = historyProps.get(gene.getXpath());
				if(props.contains(prop)){
					continue;
				}else {
					props.add(prop);
				}
			}else {
				historyProps.put(gene.getXpath(), new HashSet<>());
				historyProps.get(gene.getXpath()).add(prop);
			}

			System.out.println("\n---------------------------------------------------------------------------------------------");
			System.out.println("++++++ Gen" + XbiMainIterator.getGeneration() +", Gene " + geneCount +" of " + initialChromosome.getGenes().size() +": start processing RC <" + gene.getXpath() + ", " + prop + ", " + gene.getValue(prop) +"> "
					+ "(" + (rootCauseCount++) + " of " + gene.getPropValueMap().size() + ") ++++++");
			System.out.println("++++++ rank = " + gene.getRankInFitnessScoreImprovement(prop) + ", time budget = " + gene.getTimeBudget(prop) +
					" sec, time reqd for first run = " + gene.getTimeRequiredForProcessing(prop) + " sec ++++++");
			System.out.println("---------------------------------------------------------------------------------------------");
			long startTime = System.nanoTime();

			String value = gene.getValue(prop);

			chromosome = initialChromosome.copy();
			//optimalFitnessScore = initialChromosome.getFitnessScore();
			optimalFitnessScore = initialFitnessScore;
			optimalPartXbi = initialPartXbi;

			//optimalFitnessScore = chromosome.getFitnessScore();
			optimalValue = value;

			AVM na = new AVM();
			na.AVMSearch(this, chromosome.getGene(gene.getXpath()), prop, value);

			// update optimal map
			long endTime = System.nanoTime();

			List<OptimalRootCause> oGeneList = new ArrayList<OptimalRootCause>();

			double initialGlobalFitnessScore = initialChromosome.getGlobalFitnessScore();
//			double globalFitnessScore = initialGlobalFitnessScore;
			boolean isImproved = false;

			RootCauseList copy = initialChromosome.copy();
			copy.getGene(gene.getXpath()).updateValue(prop, optimalValue);
			double globalFitnessScore = fitnessFunction.getFitnessScoreGlobal(copy);
			if(initialPartXbi.size() > optimalPartXbi.size()){
				System.out.println("Initial global fitness score = " + initialGlobalFitnessScore);
				System.out.println("New global fitness score = " + globalFitnessScore);
				isImproved = true;
			}

			OptimalRootCause oGene = new OptimalRootCause(gene.getXpath(), prop, optimalValue, optimalFitnessScore,
					 (int) Math.ceil(Util.convertNanosecondsToSeconds(endTime - startTime)),
					isImproved, globalFitnessScore,optimalPartXbi);
			oGeneList.add(oGene);
			if(optimalMap.containsKey(gene.getXpath())){
				List<OptimalRootCause> optimalRootCauses = optimalMap.get(gene.getXpath());
				optimalRootCauses.addAll(oGeneList);
				optimalMap.put(gene.getXpath(), optimalRootCauses);
			}else {
				optimalMap.put(gene.getXpath(), oGeneList);
			}

			System.out.println("++++++ time required for processing RC <" + gene.getXpath() + ", " + prop + "> = " + Util.convertNanosecondsToSeconds(endTime - startTime) + " sec ++++++");
			System.out.println("---------------------------------------------------------------------------------------------\n");

			if(Util.doubleArrayEqual(Constants.OPTIMAL_LOCAL_FITNESS_SCORE,optimalFitnessScore) ||
					globalFitnessScore == Constants.OPTIMAL_FITNESS_SCORE)
			{
				break;
			}
		}
	}
}